package cn.lingyangwl.agile.infra.group.service;

import cn.lingyangwl.agile.infra.group.assembly.DataGroupAssembly;
import cn.lingyangwl.agile.infra.group.mapper.DataGroupMapper;
import cn.lingyangwl.agile.infra.module.group.model.entity.DataGroup;
import cn.lingyangwl.agile.infra.module.group.model.rqrs.*;
import cn.lingyangwl.agile.model.constants.CommonCons;
import cn.lingyangwl.agile.model.constants.TreeCons;
import cn.lingyangwl.agile.model.enums.DataGroupBizTypeEnum;
import cn.lingyangwl.agile.tenant.core.utils.TenantUtils;
import cn.lingyangwl.framework.mybatis.plus.utils.PageUtil;
import cn.lingyangwl.framework.tool.core.CollectionUtils;
import cn.lingyangwl.framework.tool.core.StringUtils;
import cn.lingyangwl.framework.tool.core.exception.BizException;
import cn.lingyangwl.framework.tool.core.tree.TreeUtils;
import cn.lingyangwl.framework.validation.ValidationUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.yitter.idgen.YitIdHelper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.validation.Validator;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

/**
 * 通用数据组
 *
 * @author shenguangyang
 */
@Slf4j
@Service
public class DataGroupServiceImpl extends ServiceImpl<DataGroupMapper, DataGroup>
    implements IService<DataGroup>, DataGroupService {

    @Resource
    private DataGroupAssembly dataGroupAssembly;
    @Resource
    private Validator validator;
    
    @Override
    public IPage<DataGroupDto> pageGroup(DataGroupPageReq req) {
        DataGroupBizTypeEnum bizTypeEnum = DataGroupBizTypeEnum.ofByType(req.getBizType());
        return TenantUtils.execute(bizTypeEnum.getIsolateLevel(), () -> {
            List<Long> ids = StringUtils.toList(req.getIds(), ",", Long.class);
            // 初始化默认分组
            initDefaultGroup(req.getBizType(), req.getGroupType());

            LocalDateTime beginTime = req.getBeginLocalDateTime();
            LocalDateTime endTime = req.getEndLocalDateTime();
            LambdaQueryWrapper<DataGroup> lqw = DataGroup.lqw()
                .ge(Objects.nonNull(beginTime), DataGroup::getCreatedAt, beginTime)
                .in(CollectionUtils.isNotEmpty(ids), DataGroup::getId, ids)
                .eq(StringUtils.isNotEmpty(req.getGroupType()), DataGroup::getGroupType, req.getGroupType())
                .le(Objects.nonNull(endTime), DataGroup::getCreatedAt, endTime)
                .eq(DataGroup::getBizType, req.getBizType())
                .like(StringUtils.isNotEmpty(req.getName()), DataGroup::getName, req.getName())
                .orderByAsc(DataGroup::getSortNo).orderByDesc(DataGroup::getCreatedAt);
            IPage<DataGroup> pageData = this.page(PageUtil.to(req), lqw);
            return PageUtil.to(pageData, dataGroupAssembly::toDataGroupResp);
        });

    }
    
    @Override
    public List<DataGroupDto> listGroup(DataGroupQuery req) {
        DataGroupBizTypeEnum bizTypeEnum = DataGroupBizTypeEnum.ofByType(req.getBizType());
        return TenantUtils.execute(bizTypeEnum.getIsolateLevel(), () -> {
            // 初始化默认分组
            initDefaultGroup(req.getBizType(), req.getGroupType());
            return this.baseMapper.listGroup(req)
                .stream().map(dataGroupAssembly::toDataGroupResp)
                .collect(Collectors.toList());
        });
    }

    /**
     * 初始化当前租户默认分组
     */
    public void initDefaultGroup(Integer bizType, String groupType) {
        DataGroupBizTypeEnum dataGroupEnum = DataGroupBizTypeEnum.ofByType(bizType);
        if (!dataGroupEnum.isInitDefault()) {
            return;
        }
        long count = this.count(DataGroup.lqw()
                .eq(DataGroup::getBizType, bizType)
                .eq(StringUtils.isNotEmpty(groupType), DataGroup::getGroupType, groupType)
                .eq(DataGroup::getCode, CommonCons.DataGroup.DEFAULT_CODE));
        if (count == 0) {
            DataGroup defaultGroup = new DataGroup();
            defaultGroup.setDataCount(0L).setCode(CommonCons.DataGroup.DEFAULT_CODE)
                    .setName("默认分组").setParentId(TreeCons.ROOT_PARENT_ID)
                    .setBizType(bizType).setSortNo(-10000).setGroupType(groupType);
            this.save(defaultGroup);
        }
    }

    @Override
    public List<DataGroupTreeDto> listGroupTree(DataGroupQuery req) {
        DataGroupBizTypeEnum bizTypeEnum = DataGroupBizTypeEnum.ofByType(req.getBizType());
        return TenantUtils.execute(bizTypeEnum.getIsolateLevel(), () -> {
            // 初始化默认分组
            initDefaultGroup(req.getBizType(), req.getGroupType());

            LambdaQueryWrapper<DataGroup> lqw = DataGroup.lqw()
                .eq(DataGroup::getBizType, req.getBizType())
                .eq(StringUtils.isNotEmpty(req.getGroupType()), DataGroup::getGroupType, req.getGroupType())
                .orderByAsc(DataGroup::getSortNo).orderByDesc(DataGroup::getCreatedAt);
            List<DataGroupTreeDto> flatList = this.list(lqw).stream().map(dataGroupAssembly::toDataGroupTreeResp)
                .collect(Collectors.toList());

            List<DataGroupTreeDto> treeList = TreeUtils.build(flatList, TreeCons.ROOT_PARENT_ID);

            // 过滤树
            return TreeUtils.filter(treeList, (e) -> StringUtils.isEmpty(req.getName()) || e.getName().contains(req.getName()));
        });
    }

    @Override
    public List<DataGroupDto> listChildrenGroup(Long parentId) {
        DataGroup parentGroup = this.getById(parentId);
        if (Objects.isNull(parentGroup)) {
            return Collections.emptyList();
        }

        LambdaQueryWrapper<DataGroup> lqw = DataGroup.lqw().eq(DataGroup::getBizType, parentGroup.getBizType());
        List<DataGroupTreeDto> flatList = this.list(lqw).stream().map(dataGroupAssembly::toDataGroupTreeResp)
                .collect(Collectors.toList());

        List<DataGroupTreeDto> treeList = TreeUtils.build(flatList, TreeCons.ROOT_PARENT_ID);

        // 获取查询的根节点
        AtomicReference<DataGroupTreeDto> parentTree = new AtomicReference<>();
        TreeUtils.foreachTree(treeList, (e) -> {
            if (parentTree.get() == null && e.getName().equals(parentGroup.getName())) {
                parentTree.set(e);
            }
        });

        return TreeUtils.flatTree(parentTree.get()).stream().map(dataGroupAssembly::toDataGroupResp)
                .collect(Collectors.toList());
    }

    @Override
    public DataGroupDto getGroup(Long id) {
       return TenantUtils.executeIgnore(() -> {
           DataGroupQuery query = new DataGroupQuery().setIds(Collections.singletonList(id));
           DataGroup entity = this.baseMapper.listGroup(query).stream().findFirst().orElse(null);
           return dataGroupAssembly.toDataGroupResp(entity);
       });
    }

    @Override
    public Long saveGroup(DataGroupSaveReq req) {
        ValidationUtils.validate(validator, req);
        DataGroupBizTypeEnum.checkExistByType(req.getBizType());
        DataGroup entity = dataGroupAssembly.toDataGroup(req);
        // auto 表示系统自动生成
        entity.setCode(StringUtils.firstNonEmpty(req.getCode(), "auto_" + YitIdHelper.nextId()));
        this.save(entity);
    	return entity.getId();
    }

    @Override
    public boolean updateGroup(DataGroupUpdateReq req) {
        ValidationUtils.validate(validator, req);
        DataGroup entity = dataGroupAssembly.toDataGroup(req);
        if (Objects.nonNull(req.getParentId()) && req.getParentId().compareTo(req.getId()) == 0) {
            throw new BizException("上级分组不能是自己");
        }
        LambdaUpdateWrapper<DataGroup> luw = DataGroup.luw()
                .setSql("data_count = data_count + " + req.getChangeDataCount())
                .eq(DataGroup::getId, req.getId());
        return this.update(entity, luw);
    }

    @Override
    public boolean deleteGroup(DataGroupDeleteReq req) {
        List<DataGroup> list = this.list(DataGroup.lqw().eq(DataGroup::getBizType, req.getBizType()).in(DataGroup::getId, req.getIds()));
        if (list.stream().anyMatch(e -> CommonCons.DataGroup.DEFAULT_CODE.equals(e.getCode()))) {
            throw new BizException("不能删除默认分组");
        }
        return this.removeByIds(req.getIds());
    }
}
